using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace BlockOS.Collections
{
    // a collection that returns default value when index out of bounds.
    public class DefaultArray3D<T>
    {
        private readonly T[,,] array;
        private readonly T defaultValue;

        public T this[(int x, int y, int z) pos]
        {
            get => this[pos.x, pos.y, pos.z];
            set => this[pos.x, pos.y, pos.z] = value;
        }

        public T this[int x, int y, int z]
        {
            get
            {
                if (x < 0 || y < 0 || z < 0 || x >= array.GetLength(0) || y >= array.GetLength(1) || z >= array.GetLength(2)) return defaultValue;
                return array[x, y, z];
            }

            set
            {
                if (x < 0 || y < 0 || z < 0 || x >= array.GetLength(0) || y >= array.GetLength(1) || z >= array.GetLength(2)) return;
                array[x, y, z] = value;
            }
        }

        public T GetUnchecked(int x, int y, int z)
        {
            return array[x, y, z];
        }

        public void SetUnchecked(int x, int y, int z, T value)
        {
            array[x, y, z] = value;
        }

        public void ForEach(Action<T, int, int, int> f)
        {
            for (int x = 0; x < array.GetLength(0); x++)
            {
                for (int y = 0; y < array.GetLength(1); y++)
                {
                    for (int z = 0; z < array.GetLength(2); z++)
                    {
                        f(array[x, y, z], x, y, z);
                    }
                }
            }
        }

        public void Set(Func<int, int, int, T> f)
        {
            for (int x = 0; x < array.GetLength(0); x++)
            {
                for (int y = 0; y < array.GetLength(1); y++)
                {
                    for (int z = 0; z < array.GetLength(2); z++)
                    {
                        array[x, y, z] = f(x, y, z);
                    }
                }
            }
        }

        public T[,,] ToArray()
        {
            return array;
        }

        public DefaultArray3D(T defaultValue, int lengthX, int lengthY, int lengthZ)
        {
            this.defaultValue = defaultValue;
            array = new T[lengthX, lengthY, lengthZ];
        }

        public DefaultArray3D(T defaultValue, int cubeLength) : this(defaultValue, cubeLength, cubeLength, cubeLength) { }
    }
}
